<!DOCTYPE html>
<html>
<head>
    <title>Buoyancy demo - p2.js physics engine</title>
    <script src="../build/p2.js"></script>
    <script src="../build/p2.renderer.js"></script>
    <link href="css/demo.css" rel="stylesheet"/>
    <meta name="description" content="Buoyancy aka boat simulation">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
    <script>

        // Create demo application
        var app = new p2.WebGLRenderer(function(){

            var world = new p2.World({
                gravity : [0,-10]
            });

            this.setWorld(world);

            // Create "water surface"
            var planeShape = new p2.Plane();
            var plane = new p2.Body({
                position:[0,0],
                collisionResponse: false
            });
            plane.addShape(planeShape);
            world.addBody(plane);

            body = new p2.Body({
                mass: 1,
                position: [0,2],
                angularVelocity: 0.5
            });
            body.addShape(new p2.Circle({ radius: 0.5 }), [0.5,0], 0);
            body.addShape(new p2.Circle({ radius: 0.5 }), [-0.5,0], 0);
            world.addBody(body);

            body2 = new p2.Body({
                mass: 1,
                position: [-3,2],
                angularVelocity: 1
            });
            body2.addShape(new p2.Box({ width: 0.5, height:  2 }), [1,0], 0);
            body2.addShape(new p2.Box({ width: 0.5, height:  2 }), [0.5,0], 0);
            body2.addShape(new p2.Box({ width: 0.5, height:  2 }), [-0.5,0], 0);
            body2.addShape(new p2.Box({ width: 0.5, height:  2 }), [-1,0], 0);
            world.addBody(body2);

            // Add forces every step
            world.on('postStep', function(){
                applyAABBBuoyancyForces(body, plane.position, k, c);
                applyAABBBuoyancyForces(body2, plane.position, k, c);
            });

            var shapePosition = [0,0];
            var centerOfBouyancy = [0,0];
            var liftForce = [0,0];
            var viscousForce = [0,0];
            var shapeAngle = 0;
            var k = 100; // up force per submerged "volume"
            var c = 0.8; // viscosity
            var v = [0,0];
            var aabb = new p2.AABB();
            function applyAABBBuoyancyForces(body, planePosition, k, c){
                for (var i = 0; i < body.shapes.length; i++) {

                    var shape = body.shapes[i];

                    // Get shape world transform
                    body.vectorToWorldFrame(shapePosition, shape.position);
                    p2.vec2.add(shapePosition, shapePosition, body.position);
                    shapeAngle = shape.angle + body.angle;

                    // Get shape AABB
                    shape.computeAABB(aabb, shapePosition, shapeAngle);

                    var areaUnderWater;
                    if(aabb.upperBound[1] < planePosition[1]){
                        // Fully submerged
                        p2.vec2.copy(centerOfBouyancy,shapePosition);
                        areaUnderWater = shape.area;
                    } else if(aabb.lowerBound[1] < planePosition[1]){
                        // Partially submerged
                        var width = aabb.upperBound[0] - aabb.lowerBound[0];
                        var height = 0 - aabb.lowerBound[1];
                        areaUnderWater = width * height;
                        p2.vec2.set(centerOfBouyancy, aabb.lowerBound[0] + width / 2, aabb.lowerBound[1] + height / 2);
                    } else {
                        continue;
                    }

                    // Compute lift force
                    p2.vec2.subtract(liftForce, planePosition, centerOfBouyancy);
                    p2.vec2.scale(liftForce, liftForce, areaUnderWater * k);
                    liftForce[0] = 0;

                    // Make center of bouycancy relative to the body
                    p2.vec2.subtract(centerOfBouyancy,centerOfBouyancy,body.position);

                    // Viscous force
                    body.getVelocityAtPoint(v, centerOfBouyancy);
                    p2.vec2.scale(viscousForce, v, -c);

                    // Apply forces
                    body.applyForce(viscousForce,centerOfBouyancy);
                    body.applyForce(liftForce,centerOfBouyancy);
                }
            }
        });

    </script>
</body>
</html>
